package com.alone.jpa.entity.enhancer;

import com.alone.jpa.entity.annotation.Enhanced;
import com.alone.jpa.entity.support.EntityFieldsCache;
import lombok.SneakyThrows;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;

import javax.persistence.EntityManager;
import javax.persistence.Id;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @author Alone
 */
public class UpdateIgnoreNullValueHandler implements EntityEnhanceHandler {

    @Lazy
    @Autowired
    private LocalContainerEntityManagerFactoryBean emf;

    /**
     * EntityManager缓存问题，这边另起一个查询真实数据
     */
    private volatile EntityManager em = null;

    @Override
    public void preUpdate(Object entity) {
        Class<?> clazz = entity.getClass();

        Enhanced enhanced = clazz.getDeclaredAnnotation(Enhanced.class);
        if (enhanced != null && !enhanced.isUpdateIgnoreNullValue()) {
            return;
        }

        List<Field> fields = EntityFieldsCache.getFields(clazz);
        Field id = null;
        for (Field field : fields) {
            if (field.getAnnotation(Id.class) != null) {
                id = field;
                break;
            }
        }
        if (id != null) {
            Object original = getOriginal(entity, id);
            if (original != null) {
                copyNullProperties(original, entity);
                em.detach(original);
            }
        }
    }

    @SneakyThrows
    private Object getOriginal(Object entity, Field id) {
        if (em == null) {
            synchronized (this) {
                if (em == null) {
                    em = emf.getNativeEntityManagerFactory().createEntityManager();
                }
            }
        }
        return em.find(entity.getClass(), id.get(entity));
    }

    /**
     * 将目标源中不为空的字段过滤，将数据库中查出的数据源复制到提交的目标源中
     *
     * @param source 用id从数据库中查出来的数据源
     * @param target 提交的实体，目标源
     */
    private void copyNullProperties(Object source, Object target) {
        BeanUtils.copyProperties(source, target, getNoNullProperties(target));
    }

    /**
     * @param target 目标源数据
     * @return 将目标源中不为空的字段取出
     */
    private String[] getNoNullProperties(Object target) {
        BeanWrapper srcBean = new BeanWrapperImpl(target);
        PropertyDescriptor[] pds = srcBean.getPropertyDescriptors();
        Set<String> noEmptyName = new HashSet<>();
        for (PropertyDescriptor p : pds) {
            Object value = srcBean.getPropertyValue(p.getName());
            if (value != null) {
                noEmptyName.add(p.getName());
            }
        }
        String[] result = new String[noEmptyName.size()];
        return noEmptyName.toArray(result);
    }

}
